#include "uax_net.h"

int fdClose(socket_t& fd)
{
	if (fd > 0) {
#ifdef WIN32
		closesocket(fd);
#else
		close(fd);
#endif
		fd = -1;
	}
	return -1;
}

int tcp_linger(socket_t fd)
{
	//l_onoff = 0 �����ر�
	//l_onoff = 1 �ȴ��೤ʱ��ر�
	//l_linger = n �ȴ�n��ʱ��ر�
	struct linger so_linger = { 0,0 };
	if (setsockopt(fd, SOL_SOCKET, SO_LINGER,
		(const char*)&so_linger, sizeof(so_linger))) {
		return -1;
	}
	return 0;
}

int tcp_timeout(socket_t fd, int ms)
{
#ifdef _WIN32
	if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO,
		(char*)&ms, sizeof(int))) {
		return  -1;
	}

	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO,
		(char*)&ms, sizeof(int))) {
		return  -1;
	}
#else
	timeval tTimeValue;
	tTimeValue.tv_sec = ms / 1000;
	tTimeValue.tv_usec = ms % 1000 * 1000;
	if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO,
		&tTimeValue, sizeof(timeval))) {
		return -1;
	}

	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO,
		&tTimeValue, sizeof(timeval))) {
		return -1;
	}
#endif
	return 0;
}

int tcp_keep_alive(socket_t fd)
{
	int keepalive = 1;
	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
		(char*)&keepalive, sizeof(int))) {
		return -1;
	}
#ifndef WIN32
	int tcp_keepcnt = 3;
	if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT,
		&tcp_keepcnt, sizeof(int))) {
		return -1;
	}

	int tcp_keepidle = 30;
	if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE,
		&tcp_keepidle, sizeof(int))) {
		return -1;
	}

	int tcp_keepintvl = 30;
	if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL,
		&tcp_keepintvl, sizeof(int))) {
		return -1;
	}

	int time_ack = 10 * 1000;
	if (setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT,
		&time_ack, sizeof(int))) {
		return -1;
	}
#else 
	struct tcp_keepalive {
		u_long onoff;
		u_long keepalivetime;
		u_long keepaliveinterval;
	};

	unsigned long dwByte = 0;
	tcp_keepalive alive_in = { 1 };
	tcp_keepalive alive_out = { 0 };
	alive_in.keepalivetime = 3000;  // 1s TCP_KEEPIDLE
	alive_in.keepaliveinterval = 5000; //1s TCP_KEEPINTVL
#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
	if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in),
		&alive_out, sizeof(alive_out), &dwByte, NULL, NULL)) {
		return -1;
	}
#endif // WIN32

	return 0;
}

int udp_sockopt(socket_t fd)
{
	int optval = 0x01;
#ifdef WIN32
	u_long flag = 0x01;
	ioctlsocket(fd, FIONBIO, &flag);
#else
	int flags = fcntl(fd, F_GETFL, 0);
	if (flags == -1) {
		return -1;
	}
	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
		return -1;
	}
#endif

	optval = 0x400000;
	if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
		(char*)&optval, sizeof(int))) {
		return -1;
	}

	optval = 0x400000;
	if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
		(char*)&optval, sizeof(int))) {
		return -1;
	}
#if	0
	{
		int  snd_size = 0, rcv_size = 0;
		socklen_t optlen = sizeof(snd_size);
		if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &snd_size, &optlen)) {
			return -1;
		}
		printf("udp send_buf = %d \n", snd_size);

		if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen)) {
			return -1;
		}
		printf("udp recv_buf = %d \n", rcv_size);
	}
#endif
	return 0;
}

int tcp_sockopt(socket_t fd)
{
	int optval = 0x01;
#ifdef WIN32
	u_long flag = 0x01;
	ioctlsocket(fd, FIONBIO, &flag);
#else
	int flags = fcntl(fd, F_GETFL, 0);
	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
		return -1;
	}
#endif

	if (tcp_keep_alive(fd) ||
		tcp_linger(fd)) {
		return -1;
	}

	optval = 0x01;
	if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
		(const char*)&optval, sizeof(int))) {
		return -1;
	}

	optval = 0x400000;
	if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
		(char*)&optval, sizeof(int))) {
		return -1;
	}

	optval = 0x400000;
	if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF,
		(char*)&optval, sizeof(int))) {
		return -1;
	}

#if	0
	{
		int  snd_size = 0, rcv_size = 0;
		socklen_t optlen = sizeof(snd_size);

		if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &snd_size, &optlen)) {
			return -1;
		}
		printf("tcp send_buf = %d \n", snd_size);

		if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen)) {
			return -1;
		}
		printf("tcp recv_buf fd = %d \n", rcv_size);

	}
#endif
	return 0;
}

int listen_sockopt(socket_t fd)
{
	int optval = 0x01;
#ifdef WIN32
	u_long flag = 0x01;
	ioctlsocket(fd, FIONBIO, &flag);
#else
	int flags = fcntl(fd, F_GETFL, 0);
	if (flags == -1) {
		return -1;
	}
	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
		return -1;
	}
#endif

	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
		(char*)&optval, sizeof(int))) {
		return -1;
	}
	return 0;
}

int net_localaddr(socket_t s, struct sockaddr* addr)
{
	socklen_t fdlen = sizeof(struct sockaddr_in);
	if (getsockname(s, addr, &fdlen)) {
		return -1;
	}
	return 0;
}

int net_remotaddr(socket_t s, struct sockaddr* addr)
{
	socklen_t fdlen = sizeof(struct sockaddr_in);
	if (getpeername(s, addr, &fdlen)) {
		return -1;
	}
	return 0;
}

int ipv4_address(struct sockaddr_in* addr, const char* ip, unsigned short nPort)
{
	memset(addr, 0, sizeof(struct sockaddr_in));

	addr->sin_family = AF_INET;
	addr->sin_port = htons(nPort);
	addr->sin_addr.s_addr = INADDR_ANY;

	if (NULL == ip || strlen(ip) == 0) {
		return 0;
	}
	return (inet_pton(AF_INET, ip, &addr->sin_addr) > 0) ? 0 : -1;
}

int udp_bind(socket_t* fd, const char* ip, unsigned short nPort)
{
	struct sockaddr_in addr = { 0 };
	*fd = socket(AF_INET, SOCK_DGRAM, 0);

	if (ipv4_address(&addr, ip, nPort) ||
		(*fd == -1)) {
		return -1;
	}

	if (bind(*fd, (struct sockaddr*)&addr,
		sizeof(struct sockaddr_in))) {
		return -1;
	}

	return udp_sockopt(*fd);
}

int tcp_bind(socket_t* fd, const char* ip, unsigned short nPort)
{
	struct sockaddr_in addr = { 0 };
	*fd = socket(AF_INET, SOCK_STREAM, 0);
	if (ipv4_address(&addr, ip, nPort) ||
		(*fd == -1)) {
		return -1;
	}
	if (bind(*fd, (struct sockaddr*)&addr,
		sizeof(struct sockaddr_in))) {
		return -1;
	}
	return tcp_sockopt(*fd);
}

int tcp_listen(socket_t* fd, const char* ip, unsigned short nPort)
{
	struct sockaddr_in addr = { 0 };
	if ((*fd = socket(AF_INET, SOCK_STREAM, 0)) <= 0) {
		return -1;
	}

	if (listen_sockopt(*fd) || ipv4_address(&addr, ip, nPort)) {
		return -1;
	}

	if (bind(*fd, (struct sockaddr*)&addr, sizeof(addr))) {
		return -1;
	}

	return listen(*fd, 0x400);
}

int tcp_connect(socket_t fd, const char* ip, unsigned short nPort, int tm)
{
	int nRet = -1;
	struct sockaddr_in addr;
	ipv4_address(&addr, ip, nPort);
	nRet = connect(fd, (struct sockaddr*)&addr, sizeof(addr));

#ifdef WIN32
	if ((-1 == nRet) && (WSAGetLastError() == WSAEWOULDBLOCK)) {

		fd_set fdset;
		FD_ZERO(&fdset);
		FD_SET(fd, &fdset);

		struct timeval tv = { tm,0 };
		int optval = 0, len = sizeof(int);
		if (select(fd, NULL, &fdset, NULL, &tv) > 0) {
			getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&optval, &len);
			if (optval == 0) {
				return 0;
			}
		}
	}
#else
	if ((-1 == nRet) && (errno == EINPROGRESS)) {
		struct tcp_info info;
		socklen_t len = sizeof(struct tcp_info);
		unsigned int dwTick = TimGetTicks() + tm * 1000;
		do {
			if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t*)&len) == 0) {
				if (info.tcpi_state == TCP_ESTABLISHED) {
					return 0;
				}
			}
			usleep(200);
		} while (TimGetTicks() < dwTick);
	}
#endif
	return nRet;
}

int tcp_sendmessage(socket_t fd, const char* data, int len)
{
	int dwByte, dwWrite = 0;

	do {
#ifdef WIN32
		dwByte = send(fd, data + dwWrite, len, 0);
		if (dwByte < 0) {
			if (WSAEWOULDBLOCK == WSAGetLastError()) {
				continue;
			}
			return -1;
		}
#else
		dwByte = send(fd, data + dwWrite, len, MSG_NOSIGNAL);
		if (dwByte < 0) {
			if (EAGAIN == errno || errno == EWOULDBLOCK) {
				continue;
			}
			return -1;
		}
#endif
		len -= dwByte;
		dwWrite += dwByte;
	} while (len > 0);

	return 0;
}

int udp_sendmessage(socket_t fd, const char* data, int len, sockaddr_in* addr)
{
	if (len != sendto(fd, data, len, 0,
		(struct sockaddr*)addr, sizeof(struct sockaddr_in))) {
		return -1;
	}
	return 0;
}

int thread_name(socket_t fd, const char* name)
{
#if	0
	int  snd_size = 0, rcv_size = 0;
	socklen_t optlen = sizeof(snd_size);

	if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &snd_size, &optlen)) {
		return -1;
	}
	printf("%s send_buf = %d \n", name, snd_size);

	if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen)) {
		return -1;
	}
	printf("%s recv_buf = %d \n", name, rcv_size);
#endif

#ifndef WIN32
	prctl(PR_SET_NAME, name);
#endif

	return 0;
}

uint64 ips_serial(struct sockaddr_in* addr)
{
	uint64 res = ((uint64)addr->sin_addr.s_addr << 16);
	res |= (uint64)(addr->sin_port);
	return res;
}